import {reqByFetch} from 'st-common-req'
import {IconifyJSON, IconifyInfo, IconifyMetaData} from "@iconify/types"
import {IconifySetIcons, SearchIconifyIconsParams, SearchIconifyIconsResult} from "@/utils/iconify/iconify-types.ts"
import {
  IconifyIcon,
  iconExists,
  getIcon,
  addIcon,
  addCollection,
  IconifyIconName,
  loadIcon,
  loadIcons,
  IconifyIconLoaderAbort,
  IconifyIconLoaderCallback,
  enableCache,
  disableCache,
} from "@iconify/vue"

/**
 * 获取 Iconify 指定图标集指定图标的 SVG 代码字符串
 * Iconify 文档：https://iconify.design/docs/api/svg.html
 *
 * @param {string} prefix 图标集前缀，用于指定要获取 SVG 的图标所在的图标集
 * @param {string} name 图标名称，用于指定要获取图标 SVG 的图标名称
 * @returns {Promise<string>} Iconify 指定图标集指定图标的 SVG 代码字符串
 */
export const getIconifyIconSvg = async (prefix: string, name: string): Promise<string> => {
  let url = `https://api.iconify.design/${prefix}/${name}.svg`
  return reqByFetch({url, jsonParse: false})
}

/**
 * 获取 Iconify 指定图标集指定图标的 CSS 代码字符串
 * Iconify 文档：https://iconify.design/docs/api/css.html
 *
 * @param {string} prefix 图标集前缀，用于指定要获取 CSS 的图标所在的图标集
 * @param {string[]} icons 图标名称列表，用于指定要获取 CSS 的图标名称
 * @returns {Promise<string>} Iconify 指定图标集指定图标的 CSS 代码字符串
 */
export const getIconifyIconCSS = async (prefix: string, icons: string[]): Promise<string> => {
  let url = `https://api.iconify.design/${prefix}.css?icons=${icons.join(',')}`
  return reqByFetch({url, jsonParse: false})
}

/**
 * 获取 Iconify 指定图标集中的指定图标数据
 *
 * @param {string} prefix 图标集前缀，用于指定要获取的图标数据所在的图标集
 * @param {string[]} icons 图标名称列表，用于指定要获取图标数据的图标名称
 * @returns {Promise<IconifyJSON>} Iconify 指定图标集指定图标数据对象，要获取的图标数据以对象形式存放在返回对象的 icons 属性中，其中，图标名称为
 * key，图标数据为 value
 */
export const getIconifyIconData = async (prefix: string, icons: string[]): Promise<IconifyJSON> => {
  let url = `https://api.iconify.design/${prefix}.json?icons=${icons.join(',')}&pretty=1`
  return reqByFetch({url})
}

/**
 * 获取 Iconify 指定图标集中的指定图标数据，并将获取到的每个图标数据解析为 IconifyIcon 对象，以数组形式返回
 *
 * @param {string} prefix 图标集前缀，用于指定要获取的图标数据所在的图标集
 * @param {string[]} icons 图标名称列表，用于指定要获取图标数据的图标名称
 * @returns {Promise<IconifyIcon[]>} IconifyIcon 对象数组，每个对象代表一个图标，包含 body、height、width、left、top、rotate、hFlip、vFlip 等属性
 */
export const getIconifyIconDataParse = async (prefix: string, icons: string[]): Promise<IconifyIcon[]> => {
  const iconifyIconData = await getIconifyIconData(prefix, icons)
  const iconDataList: IconifyIcon[] = []
  for (const icon in iconifyIconData.icons) {
    iconDataList.push({
      body: iconifyIconData.icons[icon].body,
      height: iconifyIconData.icons[icon].height ?? iconifyIconData.height ?? 16,
      width: iconifyIconData.icons[icon].width ?? iconifyIconData.width ?? 16,
      left: iconifyIconData.icons[icon].left ?? iconifyIconData.left ?? 0,
      top: iconifyIconData.icons[icon].top ?? iconifyIconData.top ?? 0,
      rotate: iconifyIconData.icons[icon].rotate ?? 0,
      hFlip: iconifyIconData.icons[icon].hFlip ?? false,
      vFlip: iconifyIconData.icons[icon].vFlip ?? false,
    })
  }
  return iconDataList
}

/**
 * 获取 Iconify 图标集信息对象
 * Iconify 文档：https://iconify.design/docs/api/collections.html
 *
 * @param {string[]} prefixes 图标集前缀列表，用于指定要获取的图标集
 * @returns {Promise<Record<string, IconifyInfo>>} Iconify 图标集信息对象，每个图标集的名称为 key，图标集信息为 value
 */
export const getIconifyIconSets = async (prefixes: string[]): Promise<Record<string, IconifyInfo>> => {
  let url = 'https://api.iconify.design/collections'
  if (prefixes.length > 0) url += `?prefixes=${prefixes.join(',')}&pretty=1`
  else url += `?pretty=1`
  return reqByFetch({url})
}

/**
 * 根据分类好的图标名称列表生成不进行分类的图标名称列表
 *
 * @param {Record<string, string[]>} iconifySetIconsCategories 分类好的图标名称列表
 * @returns {string[]} 不进行分类的图标名称列表
 */
export const genIconifySetIconsUncategorized = (iconifySetIconsCategories: Record<string, string[]>): string[] => {
  const iconifySetIconsUncategorized: string[] = []
  for (const category in iconifySetIconsCategories) {
    iconifySetIconsUncategorized.push(...iconifySetIconsCategories[category])
  }
  return iconifySetIconsUncategorized
}

/**
 * 获取 Iconify 指定图标集中的图标列表
 * Iconify 文档：https://iconify.design/docs/api/collection.html
 *
 * @param {string} prefix 图标集前缀，用于指定要获取的图标集
 * @param {boolean} info 在获取到的响应数据中是否包含图标集的信息，默认为 false
 * @param {boolean} uncategorized 在返回的对象中是否包含未分类的图标名称列表，默认为 true
 * @returns {Promise<IconifySetIcons>} Iconify 指定图标集图标列表信息对象
 */
export const getIconifySetIcons = async (
  {
    prefix,
    info = false,
    uncategorized = true,
  }: {
    prefix: string;
    info?: boolean;
    uncategorized?: boolean;
  }
): Promise<IconifySetIcons> => {
  let url = `https://api.iconify.design/collection?prefix=${prefix}&info=${info}&pretty=1`
  return reqByFetch({url})
    .then((iconifySetIcons: IconifySetIcons) => {
      // 如果在返回的对象中是否包含未分类的图标名称列表，同时获取到的图标集信息对象中没有包含 uncategorized 属性
      if (uncategorized && !iconifySetIcons.uncategorized) {
        // 从返回的对象数据的 categories(分类好的图标名称列表) 获取
        iconifySetIcons.uncategorized = genIconifySetIconsUncategorized(iconifySetIcons.categories as Record<string, string[]>)
      }
      // 返回 Iconify 图标集图标列表信息对象
      return iconifySetIcons
    })
    .catch((error) => Promise.reject(error))
}

/**
 * 搜索 Iconify 图标
 * Iconify 文档：https://iconify.design/docs/api/search.html
 * 如果搜索字符串 query 中包含特殊关键字，官方文档相关说明：https://iconify.design/docs/api/search.html#keywords
 *
 * @param {string} query 图标搜索字符串
 * @param {number} limit 搜索结果数量限制，默认为 64，最小值为 32，最大值为 999
 * @param {number} start 返回搜索结果的起始索引，默认为 0
 * @param {string[]} prefixes 图标集前缀列表，用于将搜索结果限制在指定图标集中，默认为全部图标集
 * @param {string} category 图标集的类别，用于将搜索结果限制在指定类别的图标集中，默认为全部类别
 * @returns {Promise<SearchIconifyIconsResult>} Iconify 图标搜索结果对象
 */
export const searchIconifyIcons = async (
  {
    query,
    limit = 64,
    start = 0,
    prefixes = [],
    category = ''
  }: SearchIconifyIconsParams
): Promise<SearchIconifyIconsResult> => {
  let url = `https://api.iconify.design/search?query=${query}&limit=${limit}&start=${start}&pretty=1`
  if (prefixes.length > 0) url += `&prefixes=${prefixes.join(',')}`
  if (category) url += `&category=${category}`
  return reqByFetch({url})
}

/**
 * 根据图标集的 IconifyMetaData 类型的数据对象中的 categories 属性生成获取未分类的图标名称列表
 *
 * @param {IconifyMetaData} metaData 图标集的 IconifyMetaData 类型的数据对象
 * @returns {string[]} 未分类的图标名称列表
 */
export const getIconifySetIconsUncategorized = (metaData: IconifyMetaData): string[] => {
  return genIconifySetIconsUncategorized(metaData.categories as Record<string, string[]>)
}

/**
 * 从图标集的 IconifyJSON 数据对象中获取图标集的所有图标名称列表
 *
 * @param {IconifyJSON} iconifyJSON 图标集的 IconifyJSON 数据对象
 * @returns {string[]} 图标集的所有图标名称列表
 */
export const getSetIconsFromIconifyJSON = (iconifyJSON: IconifyJSON): string[] => {
  return Reflect.ownKeys(iconifyJSON.icons) as string[]
}

/**
 * 检查图标数据是否可用于渲染
 *
 * @param {string} name 图标名称
 * @returns {boolean} 是否可用于渲染，如果图标可用，则返回true；如果图标不可用，则返回false。
 */
export const iconifyIconExists = (name: string): boolean => {
  return iconExists(name)
}

/**
 * 从本地存储图标数据中获取指定的图标数据
 *
 * @param {string} name 图标名称
 * @returns {IconifyIcon | null} IconifyIcon 对象，如果图标数据不存在，则返回 null
 */
export const getIconifyIcon = (name: string): IconifyIcon | null => {
  return getIcon(name)
}

/**
 * 将图标数据添加到本地存储图标数据中
 *
 * @param {string} name 图标名称
 * @param {IconifyIcon} data 图标数据
 * @returns {boolean} 是否添加成功，如果添加成功，则返回 true；如果添加失败，则返回 false。
 */
export const addIconifyIcon = (name: string, data: IconifyIcon): boolean => {
  return addIcon(name, data)
}

/**
 * 将图标集中所有图标数据添加到本地存储图标数据中
 *
 * @param {IconifyJSON} data 图标集数据
 * @param {string} provider API提供程序ID
 * @returns {boolean} 是否添加成功，如果添加成功，则返回 true；如果添加失败，则返回 false。
 */
export const addIconifyCollection = (data: IconifyJSON, provider?: string): boolean => {
  return addCollection(data, provider)
}

/**
 * 使用 Iconify API 检索获取图标数据
 *
 * @param {string | IconifyIconName} icon 图标名称
 * @returns {Promise<Required<IconifyIcon>>} 图标数据
 */
export const loadIconifyIcon = (icon: string | IconifyIconName): Promise<Required<IconifyIcon>> => {
  return loadIcon(icon)
}

/**
 * 使用 Iconify API 检索获取多个图标数据
 * 详细使用方法请参考：https://iconify.design/docs/icon-components/vue/load-icons.html
 *
 * @param {(string | IconifyIconName)[]} icons 图标名称
 * @param {IconifyIconLoaderCallback} callback 从 Iconify API 检索获取到图标数据时会调用的回调函数
 * @returns {IconifyIconLoaderAbort} 返回一个函数，可以用它来停止加载图标。例如，当正在加载自定义组件中的图标时，但组件的生命周期在图标加载
 * 之前就结束了，因此需要调用返回的函数停止加载图标，删除回调。
 */
export const loadIconifyIcons = (
  icons: (string | IconifyIconName)[],
  callback?: IconifyIconLoaderCallback
): IconifyIconLoaderAbort => {
  return loadIcons(icons, callback)
}

/**
 * 在 localStorage 和 sessionStorage 中启用 Iconify 的缓存，默认情况下，localStorage 处于启用状态，sessionStorage 处于禁用状态，当
 * 组件从 API 检索新图标时，图标数据已经存储在浏览器存储中，组件可以从缓存中检索图标，而不是发送 API 查询。
 *
 * @param {"local" | "session" | "all"} storage “local“ 表示 localStorage；“session” 表示 sessionStorage；“all” 表示同时启用
 * localStorage 和 sessionStorage，当同时启用 localStorage 和 sessionStorage 时，图标数据仅缓存在 localStorage 中。
 */
export const enableIconifyCache = (storage: 'local' | 'session' | 'all') => {
  enableCache(storage)
}

/**
 * 禁用 localStorage 和 sessionStorage 中 Iconify 的缓存，默认情况下，localStorage 处于启用状态，sessionStorage 处于禁用状态
 *
 * @param {"local" | "session" | "all"} storage “local“ 表示 localStorage；“session” 表示 sessionStorage；“all” 表示同时禁用
 * localStorage 和 sessionStorage，
 */
export const disableIconifyCache = (storage: 'local' | 'session' | 'all') => {
  disableCache(storage)
}
